package models

import (
	"database/sql"
	"fmt"
)

// NewArticle 新发表的文章
type NewArticle struct {
	Author  int    `json:"author"`
	Title   string `json:"title"`
	Content string `json:"content"`
}

// Article 文章
type Article struct {
	ID          int    `json:"id"`
	Author      string `json:"author"`
	Title       string `json:"title"`
	Content     string `json:"content"`
	LikeCount   int    `json:"like_count"`
	PublishTime string `json:"publish_time"`
	Publish     int    `json:"publish"`
}

// NewComment 新的评论
type NewComment struct {
	Article   int    `json:"article"`
	FromUser  int    `json:"from_user"`
	ToUser    int    `json:"to_user"`
	Content   string `json:"content"`
	LikeCount int    `json:"like_count"`
}

// Comment comment
type Comment struct {
	ID          int           `json:"id"`
	Article     int           `json:"article"`
	FromUser    int           `json:"from_user"`
	ToUser      sql.NullInt64 `json:"to_user"`
	Content     string        `json:"content"`
	LikeCount   int           `json:"like_count"`
	PublishTime string        `json:"publish_time"`
	Publish     int           `json:"publish"`
}

// CreateArticle ...
func CreateArticle(a *NewArticle) (id int, err error) {
	row := db.QueryRow(
		"INSERT INTO articles (author, title, content) VALUES ($1, $2, $3) returning id;",
		a.Author,
		a.Title,
		a.Content,
	)
	err = row.Scan(&id)
	return
}

// ListPublishedArticles ...
func ListPublishedArticles(limit int, offset int) (articles []*Article, err error) {
	rows, e := db.Query(
		`SELECT id, author, title, content, like_count, publish_time, publish 
		FROM public.articles WHERE publish=0 
		ORDER BY publish_time DESC LIMIT $1 OFFSET $2;`,
		limit, offset,
	)
	defer rows.Close()
	fmt.Println(rows.Next())
	if e != nil {
		err = e
		return
	}

	for rows.Next() {
		a := &Article{}
		err = rows.Scan(
			&a.ID,
			&a.Author,
			&a.Title,
			&a.Content,
			&a.LikeCount,
			&a.PublishTime,
			&a.Publish,
		)
		if err != nil {
			return
		}
		articles = append(articles, a)
	}
	return
}

// ListAllArticles ...
func ListAllArticles(limit int, offset int) (
	articles []*Article, err error) {
	rows, e := db.Query(
		`SELECT 
		id, author, title, content, 
		like_count, publish_time, publish 
		FROM public.articles WHERE publish=0 
		ORDER BY publish_time DESC LIMIT $1 OFFSET $2;`,
		limit, offset,
	)
	defer rows.Close()
	fmt.Println(rows.Next())
	if e != nil {
		err = e
		return
	}

	for rows.Next() {
		a := &Article{}
		err = rows.Scan(
			&a.ID,
			&a.Author,
			&a.Title,
			&a.Content,
			&a.LikeCount,
			&a.PublishTime,
			&a.Publish,
		)
		if err != nil {
			return
		}
		articles = append(articles, a)
	}
	return
}

// CountAllArticles ...
func CountAllArticles() (count int, err error) {
	row := db.QueryRow("SELECT count(*) FROM articles;")
	err = row.Scan(&count)
	return
}

// FreezeArticle ...
func FreezeArticle(article int) error {
	_, err := db.Exec("UPDATE articles SET publish=1 WHERE id=$1", article)
	if err != nil {
		return err
	}
	return nil
}

// UnfreezeArticle ...
func UnfreezeArticle(article int) error {
	_, err := db.Exec("UPDATE articles SET publish=0 WHERE id=$1", article)
	if err != nil {
		return err
	}
	return nil
}

// DelArticleAndRelatedComments ...
func DelArticleAndRelatedComments(article int) error {
	_, err := db.Exec(`DELETE FROM articles WHERE id=$1`, article)
	return err
}

// SelectArticle ..
func SelectArticle(id int) (a *Article, err error) {
	row := db.QueryRow(
		`SELECT id, author, title, content, 
		like_count, publish_time, publish 
		FROM public.articles WHERE id=$1`,
		id,
	)
	a = &Article{}
	err = row.Scan(
		&a.ID,
		&a.Author,
		&a.Title,
		&a.Content,
		&a.LikeCount,
		&a.PublishTime,
		&a.Publish,
	)
	if err != nil {
		return
	}
	return
}

// LikeArticle ...
func LikeArticle(id int) (count int, err error) {
	row := db.QueryRow(
		`UPDATE articles SET like_count=like_count+1 
		WHERE id=$1 returning like_count;`,
		id,
	)
	err = row.Scan(&count)
	if err != nil {
		return
	}
	return
}

// CommentOnArticle ...
func CommentOnArticle(article, from int, content string) (err error) {
	_, err = db.Exec(
		`INSERT INTO comments (article, from_user, content)
		VALUES ($1, $2, $3)`,
		article,
		from,
		content,
	)
	return
}

// ReplyOnAritcle ...
func ReplyOnAritcle(article, from, to int, content string) (err error) {
	_, err = db.Exec(
		`INSERT INTO comments (article, from_user, to_user, content)
		VALUES ($1, $2, $3, $4)`,
		article,
		from,
		to,
		content,
	)
	return
}

// LikeComment ...
func LikeComment(comment int) (count int, err error) {
	row := db.QueryRow(
		`UPDATE comments SET like_count=like_count+1 
		WHERE id=$1 returning like_count;`,
		comment,
	)
	err = row.Scan(&count)
	if err != nil {
		return
	}
	return
}

// SelectPublishedCommentsOfArticle ...
func SelectPublishedCommentsOfArticle(article, limit, offset int) (
	comments []*Comment, err error) {

	rows, e := db.Query(
		`SELECT id, from_user, to_user, content, like_count, publish_time, publish
		FROM comments WHERE article=$1 AND publish=0
		ORDER BY publish_time DESC LIMIT $2 OFFSET $3;`,
		article, limit, offset,
	)
	defer rows.Close()
	if e != nil {
		err = e
		return
	}

	for rows.Next() {
		c := &Comment{}
		err = rows.Scan(
			&c.ID,
			&c.FromUser,
			&c.ToUser,
			&c.Content,
			&c.LikeCount,
			&c.PublishTime,
			&c.Publish,
		)
		if err != nil {
			return
		}
		comments = append(comments, c)
	}
	return
}

// ListAllCommentsOfArticle ...
func ListAllCommentsOfArticle(article int) (
	comments []*Comment, err error) {
	rows, e := db.Query(
		`SELECT 
		id, from_user, to_user, content, 
		like_count, publish_time, publish 
		FROM comments WHERE article=$1
		ORDER BY publish_time`,
		article,
	)
	defer rows.Close()
	if e != nil {
		err = e
		return
	}

	for rows.Next() {
		c := &Comment{}
		err = rows.Scan(
			&c.ID,
			&c.FromUser,
			&c.ToUser,
			&c.Content,
			&c.LikeCount,
			&c.PublishTime,
			&c.Publish,
		)
		if err != nil {
			return
		}
		comments = append(comments, c)
	}
	return
}

// ListAllComments ...
func ListAllComments(limit int, offset int) (
	comments []*Comment, err error) {
	rows, e := db.Query(
		`SELECT 
		id, from_user, to_user, content, 
		like_count, publish_time, publish 
		FROM comments
		ORDER BY publish_time DESC LIMIT $1 OFFSET $2;`,
		limit, offset,
	)
	defer rows.Close()
	if e != nil {
		err = e
		return
	}

	for rows.Next() {
		c := &Comment{}
		err = rows.Scan(
			&c.ID,
			&c.FromUser,
			&c.ToUser,
			&c.Content,
			&c.LikeCount,
			&c.PublishTime,
			&c.Publish,
		)
		if err != nil {
			return
		}
		comments = append(comments, c)
	}
	return
}

// CountAllComments ...
func CountAllComments() (count int, err error) {
	row := db.QueryRow("SELECT count(*) FROM comments;")
	err = row.Scan(&count)
	return
}

// FreezeComment ...
func FreezeComment(comment int) error {
	_, err := db.Exec("UPDATE comment SET publish=1 WHERE id=$1", comment)
	if err != nil {
		return err
	}
	return nil
}

// UnfreezeComment ...
func UnfreezeComment(comment int) error {
	_, err := db.Exec("UPDATE comment SET publish=0 WHERE id=$1", comment)
	if err != nil {
		return err
	}
	return nil
}

// DelComment ...
func DelComment(comment int) error {
	_, err := db.Exec(`DELETE FROM comment WHERE id=$1`, comment)
	return err
}
